#include "EdoModulatorManager.h"

juce_ImplementSingleton (EdoModulatorManager)

EdoModulatorManager::EdoModulatorManager() : currentMidiChannel(0), currentXmlMap(0), midiThru(true)
{
	Log (T("EdoModulatorManager::ctor"));
}

EdoModulatorManager::~EdoModulatorManager()
{
	if (currentXmlMap)
	{
		deleteAndZero (currentXmlMap);
	}
	cache.clear (true);
}

void EdoModulatorManager::controllerEvent (void *ptr, const int64 newValue)
{
	Component *c = (Component *)ptr;

	const int i = c->getComponentPropertyInt (EDO_CTRLR_IDX, false, -1);

	if (i>=0)
	{		
		EdoModulator *ctrlr = cache.getUnchecked (i);
		if (ctrlr)
		{
			ctrlr->setValue (newValue);
		}
	}
	else
	{
		if (c->getComponentProperties())
		{
			if (c->getComponentProperties()->containsKey(EDO_CTRLR))
			{
				const int ctrlrType = c->getComponentPropertyInt (EDO_CTRLR, false, None);
	
				if (ctrlrType >= 0)
				{
					const int modulatorIndex = getModulatorForComponent (c);

					if (modulatorIndex >= 0)
					{
						cache[modulatorIndex]->setAssosiatedComponent (c);
						c->setComponentProperty (EDO_CTRLR_IDX, modulatorIndex);
					}
					else
					{
						c->setComponentProperty (EDO_CTRLR_IDX, -1);
						return;
					}
				}
				else
				{
					c->setComponentProperty (EDO_CTRLR_IDX, -1);
					return;
				}
			}
		}
	}
}

void EdoModulatorManager::registerController (Component *componentToRegister, const int64 initialValue)
{
	controllerEvent (componentToRegister, initialValue);
}

void EdoModulatorManager::setMidiChannel (const uint8 &newMidiChannel)
{
	currentMidiChannel = newMidiChannel;
}

void EdoModulatorManager::clearCache()
{
	cache.clear();
}

void EdoModulatorManager::restoreMapping (XmlElement *mappingState)
{
	currentXmlMap = mappingState;

	forEachXmlChildElement (*mappingState, el)
	{
		if (el->hasTagName (T("values")))
		{
			addStaticValues (el);
		}

		if (el->hasTagName (T("modulators")))
		{
			addModulator (el);
		}
	}
}

void EdoModulatorManager::addStaticValues (XmlElement *valuesElement)
{
	forEachXmlChildElement (*valuesElement, value)
	{
		if (value->hasTagName (T("value")))
		{
			EdoModulator *e = new EdoModulator (None);
			e->setName (value->getStringAttribute (T("name")));
			e->fromString (value->getAllSubText());

			staticValues.add (e);
		}
	}
}

void EdoModulatorManager::addModulator (XmlElement *modulatorElement)
{
	forEachXmlChildElement (*modulatorElement, modulator)
	{
		if (modulator->hasTagName (T("modulator")))
		{
			EdoModulator *e = new EdoModulator (stringToMidiType (strAttr("type")));
			e->setName (strAttr ("name"));
			e->fromString (strAttr ("formula"));

			cache.add (e);
		}
	}
}

const EdoModulatorManager::MidiMessageType EdoModulatorManager::stringToMidiType (const String &typeDescirption)
{
	if (typeDescirption == T("sysex"))
	{
		return (MidiSysEx);
	}
	return (None);
}

const int EdoModulatorManager::getModulatorForComponent(Component *componentToMatch)
{
	for (int i=0; i<cache.size(); i++)
	{
		if (cache[i]->getName() == componentToMatch->getName())
		{
			return (i);
		}
	}
	return (-1);
}

EdoModulator *EdoModulatorManager::getStaticValue (const String &name)
{
	for (int i=0; i<staticValues.size(); i++)
	{
		if (staticValues[i]->getName() == name)
		{
			return (staticValues[i]);
		}
	}

	return (0);
}

void EdoModulatorManager::addMessageToQueue (const uint8 *dataToAdd, const int dataSize)
{
	midiBuffer.addEvent (dataToAdd, dataSize, 0);
}

void EdoModulatorManager::processBlock(MidiBuffer& midiMessages)
{
	if (midiThru)
	{
		midiMessages.addEvents (midiBuffer, 0, -1, 0);
	}
	else
	{
		midiMessages.swap (midiBuffer);
	}

	midiMessages.clear();
}